package com.piece.module.spider.service;

import com.piece.core.framework.enums.StatusType;
import com.piece.core.jpa.repository.BaseService;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.net.HtmlUtil;
import com.piece.module.spider.model.SearchRule;
import com.piece.module.spider.response.SearchRuleRepository;
import com.piece.module.spider.support.MagnetResult;
import org.seimicrawler.xpath.JXDocument;
import org.seimicrawler.xpath.JXNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Service
@Transactional
public class MagnetSearchService extends BaseService<SearchRule, Integer> {

    @Autowired
    private SearchRuleRepository searchRuleRepository;

    private String[] sizeUnit = new String[]{"B|bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};

    @PostConstruct
    public void initRepository() {
        super.setRepository(searchRuleRepository);
    }

    public List<MagnetResult> search(String ruleIds, String keyword, String page) {
        List<MagnetResult> magnetResults = new ArrayList<>();
        if (StringUtil.isNotEmpty(ruleIds)) {
            Long[] ruleIdArray = Convert.toLongArray(ruleIds);
            List<SearchRule> rules = searchRuleRepository.findByKeys(Arrays.asList(ruleIdArray));
            String url;
            JXDocument jxDocument;
            for (SearchRule rule : rules) {
                url = rule.getUrl() + rule.getPaths().replaceAll("\\{k\\}", keyword).replaceAll("\\{p\\}", page);
                jxDocument = grepFromUrl(url);
                if (jxDocument != null) {
                    parseItemsDocument(jxDocument, rule, magnetResults);
                }
            }
        }
        return magnetResults;
    }

    private JXDocument grepFromUrl(String url) {
        return HtmlUtil.createByUrl(url);
    }

    private void parseItemsDocument(JXDocument document, SearchRule rule, List<MagnetResult> magnetResults) {
        List<JXNode> groups = document.selN(rule.getGroupXpath());
        groups.forEach(node -> {
            MagnetResult magnetResult = new MagnetResult();
            magnetResult.setTitle(HtmlUtil.getTextByNodeWithXpath(node, rule.getTitleXpath()));
            magnetResult.setDate(HtmlUtil.getTextByNodeWithXpath(node, rule.getDateXpath()));
            magnetResult.setSize(HtmlUtil.getTextByNodeWithXpath(node, rule.getSizeXpath()));
            if (StatusType.NORMAL.getCode().equals(rule.getDetail())) {
                String url = HtmlUtil.getHrefByNodeWithXpath(node, rule.getTitleXpath() + "/@href");
                if (StringUtil.isNotEmpty(url)) {
                    url = url.startsWith("http") ? url : rule.getUrl() + url;
                    JXDocument jxDocument = grepFromUrl(url);
                    // 查询详情页
                    parseDetailDocument(jxDocument, rule, magnetResult);
                }
            } else if (StringUtil.isNotEmpty(rule.getMagnetXpath())) {
                String magnet = HtmlUtil.getHrefByNodeWithXpath(node, rule.getMagnetXpath() + "/@href");
                magnet = magnet.startsWith("http") ? magnet : HtmlUtil.getDomainFromURL(node.asElement().baseUri()) + magnet;
                magnetResult.setMagnet(magnet);
            } else if (StringUtil.isNotEmpty(rule.getAccessCode())) {
                magnetResult.setAccessCode(HtmlUtil.getTextByNodeWithXpath(node, rule.getAccessCode()));
            }
            magnetResults.add(magnetResult);
        });
    }

    private void parseDetailDocument(JXDocument document, SearchRule rule, MagnetResult magnetResult) {
        JXNode root = document.selNOne(rule.getRootXpath());
        if (null != root) {
            if (StringUtil.isEmpty(magnetResult.getMagnet())) {
                String magnet = HtmlUtil.getHrefByNodeWithXpath(root, rule.getMagnetXpath());
                //magnet = magnet.startsWith("http") ? magnet : HtmlUtil.getDomainFromURL(root.asElement().baseUri()) + magnet;
                magnetResult.setMagnet(magnet);
            }
            if (StringUtil.isNotEmpty(rule.getAccessCode())) {
                magnetResult.setAccessCode(HtmlUtil.getTextByNodeWithXpath(root, rule.getAccessCode()));
            }
            List<String> files = HtmlUtil.getTextsByNodeWithXpath(root, rule.getFilesXpath());
            files.forEach(file -> {
                String[] fileNode = splitByFileSize(file);
                if (null != fileNode) {
                    magnetResult.addFile(fileNode[0], fileNode[1]);
                } else {
                    magnetResult.addFile(file, "");
                }
            });
        }
    }

    private String[] splitByFileSize(String text) {
        String pattern = StringUtil.join(sizeUnit, "|");
        pattern = "^(\\d+(\\.\\d+)?) {1,3}(" + pattern + ")$";
        String[] result = null;
        List<String> file = StringUtil.extractMessageByRegular(text, pattern);
        if (null != file && file.size() > 1) {
            result = new String[]{file.get(0), file.get(1)};
        }
        return result;
    }

    /**
     * 校验引擎名称是否唯一
     *
     * @param id 主键
     * @param name 引擎名称
     * @return true: 通过 false: 重复
     */
    public boolean checkNameUnique(Long id, String name) {
        String hql = "from SearchRule where name = ?0";
        Object[] params = new Object[]{name};
        if (null != id) {
            hql = "from SearchRule where id != ?0 and name = ?1";
            params = new Object[]{id, name};
        }
        long count = searchRuleRepository.count(hql, params, true);
        return count <= 0;
    }

    /**
     * 校验引擎地址是否唯一
     *
     * @param id 主键
     * @param url    url
     * @return true: 通过 false: 重复
     */
    public boolean checkUrlUnique(Long id, String url) {
        String hql = "from SearchRule where url = ?0";
        Object[] params = new Object[]{url};
        if (null != id) {
            hql = "from SearchRule where id != ?0 and url = ?1";
            params = new Object[]{id, url};
        }
        long count = searchRuleRepository.count(hql, params, true);
        return count <= 0;
    }
}
